/**
 * @file icon-button
 * @author liyong18@baidu.com
 */
/* eslint fecs-indent: [0, "space", 2, 2] */

import * as React from 'react';
import {
  PureComponent,
  PropTypes
} from 'react';
import {Renderer, renderChildren, filter} from '../renderer';
import * as omit from 'lodash/omit';
import {findDOMNode} from 'react-dom';
import * as assign from 'object-assign';
import * as cx from 'classnames';
import * as schema from './chart-schema.json';
import * as actions from './actions';
import {push, goBack} from 'react-router-redux';
import {sendNotify} from '../../actions/asist';
import onExpression from '../hoc/on-expression';
import reducer from './reducer';
import * as omit from 'lodash/omit';
import {resizeSensor} from '../../util/resize-sensor';
import './chart.scss';

@Renderer({
  name: 'chart',
  desc: '...',
  test: /(?:^|\/)chart$/,
  reducer,
  schema
}, null, assign({
  push,
  goBack,
  sendNotify
}, actions))
@onExpression
export default class ServiceRenderer extends PureComponent {
  static propTypes = {
    className: PropTypes.string,
    body: PropTypes.any,
    data: PropTypes.any,
    locals: PropTypes.any,
    api: PropTypes.any,
    schemaApi: PropTypes.any,
    onDataFilter: PropTypes.func
  };
  
  static defaultProps = {
    className: '',
    initFetch: true,
    messages: {},
    style: null,
    interval: 0,
    echartOption: {
      notMerge: true
    }
  };

  static contextTypes = {
    scoped: PropTypes.object.isRequired
  };

  fetching = false;
  container = null;

  timer = null;
  constructor(props) {
    super(props);

    this.state = {
      config: assign({}, props.config),
      data: props.data
    };

    this.reload = this.reload.bind(this);
    this.receive = this.receive.bind(this);
    this.renderChart = this.renderChart.bind(this);
  }

  componentWillMount() {
    this.context.scoped.registerComponent(this);
  }

  componentDidMount() {
    const {
      initFetch,
      chartRef,
      eChartRef,
    } = this.props;

    if (initFetch) {
      this.reload();
    }

    const container = findDOMNode(this.container);
    require(['echarts'], (echarts) => {
      window.echarts = echarts;

      this.echartInstance = echarts.init(container);
      this.setState({
        chart: true
      }, this.renderChart);

      this.unSensor = resizeSensor(container, () => {
        const width = container.offsetWidth;
        const height = container.offsetHeight;
        this.echartInstance.resize({
          width,
          height
        });
      });

      eChartRef && eChartRef(this.echartInstance);
    });

    chartRef && chartRef(this);
  }


  renderChart() {
    if (this.state.chart && this.state.config && this.echartInstance) {
      let config = this.state.config;
      if (typeof config === 'string') {
        config = (new Function("return " + config))();
      }

      if (typeof this.props.onDataFilter === 'function') {
        config = this.props.onDataFilter(config) || config;
      }

      // this.echartInstance.clear();
      this.echartInstance.setOption(config, this.props.echartOption);
    }
  }


  componentWillReceiveProps(nextProps, prevState) {
    const {
      api,
      fetchData,
      scopedKey,
      locals,
      state,
      sendNotify,
      messages,
      initFetch,
      config
    } = nextProps;
    const props = this.props;
    const action = this.state.action;

    // 注意， assign 的顺序不一样。
    if (props.state.data !== state.data) {
      let data = state.data;
      (typeof data !== 'string') && assign({}, config, state.data);
      this.setState({
        config: data
      }, this.renderChart);
    } else if (this.props.data !== nextProps.data) {
      this.setState({
        data: assign({}, this.state.data, nextProps.data)
      });
    }

    if (
      !this.fetching &&
      typeof api === 'string'
      && ~api.indexOf('$')
      && prevState.data !== this.state.data
      && filter(api, prevState.data) !== filter(api, this.state.data)
    ) {
      this.fetching = true;
      fetchData(scopedKey, api, this.state.data, locals);
    }

    // 提示获取数据情况
    if (props.state.fetching && !nextProps.state.fetching) {
      this.fetching = false;
      if (state.invalid) {
        let msg = messages.fetchFailed || (state.error instanceof Error ? state.error.message : state.message) || '获取图标配置失败';
        sendNotify({
          message: msg,
          level: 'error'
        });
      } else {
        if (messages.fetchSuccess) {
          sendNotify({
            message: messages.fetchSuccess,
            level: 'success'
          });
        }

        nextProps.interval && (this.timer = setTimeout(this.reload, Math.max(nextProps.interval, 3000)));
      }
    }

    // action 提示
    if (props.state.action.saving && !nextProps.state.action.saving) {
      const actionState = state.action;

      if (actionState.invalid) {
        sendNotify({
          message: action.messages && action.messages.failed || (actionState.error instanceof Error ? actionState.error.message : actionState.message) || '操作失败',
          level: 'error'
        });
      } else {
        sendNotify({
          message: action.messages && action.messages.success || actionState.message || '操作成功',
          level: 'success'
        });

        action.reload && this.context.scoped.reload(action.reload);
      }
    }
  }

  componentDidUpdate(prevProps) {
    const props = this.props;

    if (this.echartInstance && prevProps.state.fetching !== props.state.fetching) {
      this.echartInstance[props.state.fetching ? 'showLoading' : 'hideLoading']();
    }
  }

  componentWillUnmount() {
    this.context.scoped.unRegisterComponent(this);
    this.unSensor && this.unSensor();
    this.echartInstance && this.echartInstance.dispose();
    clearTimeout(this.timer);
  }
  

  reload() {
    const {scopedKey, api, fetchData, locals} = this.props;
    const {data} = this.state;

    if (api && !this.fetching) {
      this.fetching = true;
      fetchData(scopedKey, api, data, locals);
    }
  }

  receive(data) {
    this.setState({
      data: assign({}, this.state.data, data)
    }, this.reload);
  }

  render() {
    let {
      className,
      style,
      width,
      height
    } = this.props;
    const chart = this.state.chart;

    if (width || height) {
      style = style || {};
      style.width = width;
      style.height = height;
    }

    return (
      <div
        ref = {(ins) => this.container = ins}
        className={cx('chart text-center', className)}
        style={style}
      >
        <i key="loading" className="fa fa-spinner fa-spin fa-2x fa-fw" />
      </div>
    );
  }
}
